package com.banmajio.util;

import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
public class SnowflakeIdGeneratorUtils {

    // 起始时间戳（2025-01-01 00:00:00 UTC）
    private static final long START_TIMESTAMP;
    static {
        LocalDateTime fixedDateTime = LocalDateTime.of(2025, 1, 1, 0, 0, 0);
        ZonedDateTime zonedDateTime = fixedDateTime.atZone(ZoneId.of("UTC"));
        START_TIMESTAMP = zonedDateTime.toInstant().toEpochMilli();
    }

    // 机器ID所占位数
    private static final long WORKER_ID_BITS = 5L;
    // 序列号所占位数
    private static final long SEQUENCE_BITS = 12L;

    // 机器ID最大值
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    // 序列号最大值
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    // 时间戳左移位数
    private static final long TIMESTAMP_LEFT_SHIFT = WORKER_ID_BITS + SEQUENCE_BITS;
    // 机器ID左移位数
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;

    // 时钟回拨最大容忍毫秒数（5毫秒）
    private static final long MAX_BACKWARD_MS = 5;

    // 机器ID
    private final long workerId;

    // 预生成ID队列
    private final BlockingQueue<Long> idQueue = new LinkedBlockingQueue<>(2000);

    // 线程池
    private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(
            1,
            new ThreadFactory() {
                private final ThreadGroup group = new ThreadGroup("snowflake-id-generator");
                private final AtomicInteger threadNumber = new AtomicInteger(1);

                @Override
                public Thread newThread(Runnable r) {
                    Thread t = new Thread(group, r,
                            "snowflake-id-pre-generator-" + threadNumber.getAndIncrement(), 0);
                    t.setDaemon(true);
                    if (t.getPriority() != Thread.NORM_PRIORITY) {
                        t.setPriority(Thread.NORM_PRIORITY);
                    }
                    return t;
                }
            }
    );

    // ID生成状态
    private volatile long lastTimestamp = -1L;
    private volatile long sequence = 0L;

    // 时钟回拨计数器
    private final AtomicInteger backwardCount = new AtomicInteger(0);

    // 同步锁
    private final Object lock = new Object();

    // 单例实例
    private static volatile SnowflakeIdGeneratorUtils INSTANCE;

    /**
     * 构造方法，初始化机器ID
     * @param workerId 机器ID，范围0-31
     */
    private SnowflakeIdGeneratorUtils(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("Worker ID 必须在 0 - %d 之间", MAX_WORKER_ID));
        }
        this.workerId = workerId;

        // 启动预生成线程
        executor.scheduleAtFixedRate(this::preGenerateIds, 0, 100, TimeUnit.MILLISECONDS);

        // 添加JVM关闭钩子，优雅关闭线程池
        Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
    }

    /**
     * 获取单例实例
     * @return 单例实例
     */
    public static SnowflakeIdGeneratorUtils getInstance() {
        if (INSTANCE == null) {
            synchronized (SnowflakeIdGeneratorUtils.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SnowflakeIdGeneratorUtils(1);
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 预生成ID到队列
     */
    private void preGenerateIds() {
        try {
            // 当队列中的ID数量少于阈值时，生成一批新ID
            while (idQueue.size() < 1600) {
                idQueue.put(generateId());
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("预生成ID线程被中断");
        } catch (Exception e) {
            log.error("预生成ID发生异常", e);
        }
    }

    /**
     * 获取下一个ID
     * @return 生成的ID
     */
    public long nextId() {
        try {
            // 从队列中获取预生成的ID
            Long id = idQueue.poll(100, TimeUnit.MILLISECONDS);
            if (id != null) {
                return id;
            }

            // 队列中没有可用ID，直接生成
            log.warn("预生成ID队列空，直接生成ID");
            return generateId();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("获取预生成ID被中断，将直接生成");
            return generateId();
        }
    }

    /**
     * 生成ID的核心方法（线程安全）
     */
    private long generateId() {
        synchronized (lock) {
            long currentTimestamp = System.currentTimeMillis();

            // 处理时钟回拨
            if (currentTimestamp < lastTimestamp) {
                handleClockBackward(currentTimestamp, lastTimestamp);
                return generateId(); // 递归重试
            }

            // 同一毫秒内序列号递增
            if (lastTimestamp == currentTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0) {
                    // 序列号溢出，等待下一毫秒
                    currentTimestamp = waitNextMillis(lastTimestamp);
                }
            } else {
                // 时间戳改变，重置序列号
                sequence = 0;
            }

            lastTimestamp = currentTimestamp;

            return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
                    | (workerId << WORKER_ID_SHIFT)
                    | sequence;
        }
    }

    /**
     * 处理时钟回拨
     */
    private void handleClockBackward(long current, long last) {
        int count = backwardCount.incrementAndGet();
        long offset = last - current;

        if (offset <= MAX_BACKWARD_MS) {
            // 容忍范围内的时钟回拨，等待时钟恢复
            log.warn("检测到时钟回拨 {}ms，尝试等待恢复（第{}次）", offset, count);
            try {
                Thread.sleep(offset + 1); // 等待稍长于回拨时间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        } else {
            // 超出容忍范围，抛出异常
            throw new RuntimeException(String.format(
                    "检测到时钟回拨 %dms，超出最大容忍范围 %dms", offset, MAX_BACKWARD_MS));
        }
    }

    /**
     * 等待下一毫秒
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 优雅关闭
     */
    private void shutdown() {
        log.info("Snowflake ID生成器正在关闭...");
        executor.shutdown();
        try {
            if (!executor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        log.info("Snowflake ID生成器已关闭");
    }
}